﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
using Citi.GocAttributes.Domain.Utils;
using Citi.Import.Util;

namespace Citi.GocAttributes.Domain
{
    public partial class ManagedGeography
    {
        #region Import Validation

        private const string IdColumnName = "MANAGED_GEOGRAPHY ID";
        private const string DescriptionColumnName = "DESCRIPTION";
        private const string ParentIdColumnName = "PARENT_ID";
        private const string UpdatedDateColumnName = "EFFECTIVE DATE";
        private const string TreeLevelColumnName = "Tree Level";
        private const string StatusColumnName = "STATUS";

        private static readonly List<Definition<ManagedGeography>> Definitions = new List<Definition<ManagedGeography>>
            {
                new Definition<ManagedGeography> { ModelProperty = x => x.Code, MaxLength = 10, Required = true, ColumnName = IdColumnName, CustomConversionMethod = ValidateManagedGeographyId},
                new Definition<ManagedGeography> { ModelProperty = x => x.Description,MaxLength = 250, Required = true, ColumnName = DescriptionColumnName},
                new Definition<ManagedGeography> { ModelProperty = x => x.ParentId, ColumnName = ParentIdColumnName, CustomConversionMethod = ValidateParentManagedGeography},
                new Definition<ManagedGeography> { ModelProperty = x => x.TreeLevel, Required = true, ColumnName = TreeLevelColumnName},
                new Definition<ManagedGeography> { ModelProperty = x => x.Status, MaxLength = 1, Required = true, ColumnName = StatusColumnName},
                new Definition<ManagedGeography> { ModelProperty = x => x.UpdatedDate, Required = true, ColumnName = UpdatedDateColumnName, Culture = new CultureInfo("pt-BR")}
            };

        private static readonly List<Definition<ManagedGeography>> DefinitionsWithoutParent = new List<Definition<ManagedGeography>>
            {
                new Definition<ManagedGeography> { ModelProperty = x => x.Code, MaxLength = 10, Required = true, ColumnName = IdColumnName, CustomConversionMethod = ValidateManagedGeographyId},
                new Definition<ManagedGeography> { ModelProperty = x => x.Description,MaxLength = 250, Required = true, ColumnName = DescriptionColumnName},
                new Definition<ManagedGeography> { ModelProperty = x => x.TreeLevel, Required = true, ColumnName = TreeLevelColumnName},
                new Definition<ManagedGeography> { ModelProperty = x => x.Status, MaxLength = 1, Required = true, ColumnName = StatusColumnName},
                new Definition<ManagedGeography> { ModelProperty = x => x.UpdatedDate, Required = true, ColumnName = UpdatedDateColumnName, Culture = new CultureInfo("pt-BR")}
            };

        private static object ValidateManagedGeographyId(ref ManagedGeography model, string value, List<string> fields, ref string error, List<string> headers = null)
        {
            var domain = CacheFacade.Get(SelectAll<ManagedGeography>).OrderByDescending(x => x.ReferenceDate).FirstOrDefault(x => x.Code == value);

            if (domain != null)
            {
                if (!domain.Gocs.Any())
                    model = domain;
                else
                {
                    var description = fields[headers.IndexOf(Conversion.TransformColumnName(DescriptionColumnName))];
                    var status = fields[headers.IndexOf(Conversion.TransformColumnName(StatusColumnName))];

                    if (domain.Description == description && domain.Status == status)
                        model = domain;
                }
            }

            return value;
        }

        private static object ValidateParentManagedGeography(ref ManagedGeography model, string value, List<string> fields, ref string error, List<string> headers = null)
        {
            try
            {
                var id = fields[headers.IndexOf(Conversion.TransformColumnName(IdColumnName))];

                if (id == value)
                    return Conversion.Validation.Validated;

                var domain = CacheFacade.Get(SelectAll<ManagedGeography>).OrderByDescending(x => x.ReferenceDate)
                    .FirstOrDefault(x => x.Code == value);

                if (domain == null)
                {
                    error = string.Format("PARENT ID '{0}' NOT FOUND", value);
                    return Conversion.Validation.Failed;
                }

                model.Parent = domain;

                return Conversion.Validation.ManuallyValidated;
            }
            catch (Exception e)
            {
                error = e.Message;
                return Conversion.Validation.Failed;
            }
        }

        #endregion

        public static ImportHistory Import(string path, DateTime referenceDate)
        {
            List<Error> errors = null;
            var dataTable = Conversion.ReadExcel(path, ref errors, "Attributes", 5);

            Save(DefinitionsWithoutParent, dataTable, ref errors, referenceDate);

            errors = null;
            Save(Definitions, dataTable, ref errors, referenceDate);

            return ImportHistory.SaveHistory(errors, path);
        }

        private static void Save(List<Definition<ManagedGeography>> definitions, DataTable dataTable, ref List<Error> errors, DateTime referenceDate)
        {
            var managedGeographies = Conversion.Convert(definitions, dataTable, ref errors).Where(x => !string.IsNullOrEmpty(x.Description)).ToList();

            if (managedGeographies != null && managedGeographies.Any())
            {
                managedGeographies.ForEach(x =>
                    {
                        x.LastUpdate = DateTime.Now;

                        if (x.Id == 0)
                            x.ReferenceDate = referenceDate;
                    });

                managedGeographies.First().Save(managedGeographies, 800);
            }

            CacheFacade.Clear(typeof(ManagedGeography));
        }

        private static void SaveImport(List<ManagedGeography> managedGeographys, DateTime referenceDate, ref List<Error> errors)
        {
            managedGeographys.ForEach(x =>
            {
                x.LastUpdate = DateTime.Now;

                if (x.Id == 0)
                    x.ReferenceDate = referenceDate;
            });

            SaveModifieds();

            var lastTreeLevel = managedGeographys.Max(x => x.TreeLevel);

            for (var level = 1; level <= lastTreeLevel; level++)
            {
                var managedsNextLevel = managedGeographys.Where(x => x.TreeLevel == level).ToList();

                if (level > 1)
                {
                    var managedGeographysPrevious = managedGeographys.Where(x => x.TreeLevel == level - 1);
                    managedsNextLevel.ForEach(x =>
                    {
                        if (x.ParentId != 0 || (x.ParentId == 0 && x.Parent == null)) return;

                        var managedGeographyPrevious = managedGeographysPrevious.FirstOrDefault(y => y.Code == x.Parent.Code);
                        if (managedGeographyPrevious == null) return;

                        x.Parent = managedGeographyPrevious;
                    });
                }

                if (managedsNextLevel.Any())
                    managedsNextLevel.First().Save(managedsNextLevel, 800);
            }
        }

        private static void SaveModifieds()
        {
            var objectStateEntries = Context.Instance.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).ToList();
            objectStateEntries.AddRange(Context.Instance.ObjectStateManager.GetObjectStateEntries(EntityState.Added));

            foreach (var objectStateEntry in objectStateEntries.Where(x => x.Entity.GetType() == typeof(ManagedGeography) &&
                (((ManagedGeography)x.Entity).Description == null || ((ManagedGeography)x.Entity).Status == null
                || (((ManagedGeography)x.Entity).Parent != null && ((ManagedGeography)x.Entity).Parent.Description == null))))
            {
                Context.Instance.Detach(objectStateEntry.Entity);
            }

            Context.Instance.SaveChanges();
        }
    }
}
